/*
 This program is submitted as part of an assignment in the
 J2E
 Fall Semester 2010-2011 
 School of Computer Science
 Interdisciplinary Center, Hertzelia, Israel 

 http://www.idc.ac.il 

 Exercise No.         : 3
 File Name            : ClientController.java
 Names (First Last)   : Itay Sorani, Nadav Ziv, Yonathan Zarsky
 Student No.          : 066530254, 039937883, 039692918
 */

package com.example;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

import com.example.View.GameView;
import com.example.models.Building;
import com.example.models.Tower;
import com.example.models.Unit;
import com.example.util.Figure;
import com.example.util.FigureType;
import com.example.util.GameModel;
import com.example.util.TileType;
import com.example.util.UserRequestListener;
import com.example.util.Vec2D;

/**
 * Although the main controller of the game is on the sever side, the client needs to have a small controller,
 * which holds the view and the models of the game.
 * Update from the sever ever 500ms while updating the models ever 40ms.
 * Init update starts after 3s from loading the game.
 */
public class ClientController implements Runnable {
	
	// the time in ms between updates.
	private final static int LOOP_UPDATE_FROM_SERVER_RATE = 350;
	// the time in ms between updates.
	private final static int LOOP_UPDATE_RATE = 40;
	private static final String POINTS = "Points: ";
	private static final String PLAYER_HIT_POINTS = "Player Hit Points: ";
	private static final String ENEMY_HIT_POINTS = "Enemy Hit Points: ";
	// message for game over
	private static final String GAME_OVER = "GAME OVER";
	protected static final String IN_GAME_MESSAGE = "IN GAME MESSAGE ";
	// reference to the game view
	private GameView gameView;
	// an array holding game figures
	private GameModel models;
	
	// gets the models
	public GameModel getModels() {
		return models;
	}


	private GameControllerProxy proxy;
	private TextView points, playerHitPoints, enemyHitPoints;
	private int playerPointsInt, playerHitPointsInt, enemyHitPointsInt;
	private int timeTillNextServerUpdate = LOOP_UPDATE_FROM_SERVER_RATE * 6;
	private Context context;
	private Handler handler = new Handler();
	private String messageToDisplay = "";
	
	/**
	 *  Controller constructor.
	 *  Receives the context as an input
	 */
	public ClientController(GameControllerProxy proxy, Context context) {
		this.context = context; 
		this.proxy = proxy;
		proxy.setListener(new GameControllerListener() {
			public void onResult(TaskResult result) {
				if (result != null) {
					if (result.isSuccess()) {
						setModels(result.getModel());
						setPlayerPointsInt(result.getPlayerPoints());
						setPlayerHitPointsInt(result.getPlayerHitPoints());
						setEnemyHitPointsInt(result.getEnemyHitPoints());
						setModels(result.getModel());
						setMessagToDisplay(result.getMessageToDisplay());
						if (result.isCanvasMoving() == true) {
							gameView.moveCanvasToDestination(new Vec2D(result.getxCanvasMoveDestination(),result.getyCanvasMoveDestination()));
							Log.v ("Msg:","Moving Canvas");
						}
					}
					else {
						Log.v("Msg:","Invalid Request");
					}
				}
			}
	       });
		proxy.performInit();
	}
	
	/**
	 *  the game main loop. update every LOOP_UPDATE_RATE.
	 */
	public void gameLoop() {
		while (true) {
			if (timeTillNextServerUpdate <= 0) {
				updateFromServer();
				handler.post(new Runnable() {
					@Override
					public void run() {
						points.setText(POINTS + playerPointsInt);
				        playerHitPoints.setText(PLAYER_HIT_POINTS + playerHitPointsInt) ;
				        enemyHitPoints.setText(ENEMY_HIT_POINTS + enemyHitPointsInt) ;
					
				        if(messageToDisplay.startsWith(IN_GAME_MESSAGE) && messageToDisplay != ""){
				        	messageToDisplay = messageToDisplay.substring(IN_GAME_MESSAGE.length());
				        	gameView.displayMesssage(messageToDisplay, new Vec2D(0,250));
				        }

				        if(messageToDisplay.startsWith(GAME_OVER) && messageToDisplay != ""){
				        	gameView.displayMesssage(messageToDisplay, new Vec2D(0,250));
		    				((Activity)context).finish();
				        } 
					}
				});
				timeTillNextServerUpdate = LOOP_UPDATE_FROM_SERVER_RATE;
			} else {
				timeTillNextServerUpdate -= LOOP_UPDATE_RATE;
				update();
			}
			try {
				Thread.sleep(LOOP_UPDATE_RATE);
			} catch (InterruptedException e) {
				 
			}
		}
	}

	// updates the form server
	private void updateFromServer() {
			proxy.performInit();
	}

	/**
	 *  update all the game main figures according to their type.
	 */
	public void update() {
		Figure figure;
		if (models != null) {
			for (int i = 0; i < models.getNumOfModels(); i++) {
				figure = models.getModel(i);
				if (figure != null) {
					//if its a building
					if (figure instanceof Building) {
					}

					//if its a unit
					if (figure instanceof Unit) {
						Unit unit = (Unit)figure;
						moveUnit(unit);
					}
					
					//if its a unit
					if (figure.getFigureType() == FigureType.Shot) {
						moveShot(figure);
					}

					//if its a tower
					if (figure instanceof Tower) {
					}
				}
			}
			gameView.setGameModel(models);
			gameView.postInvalidate();
		}
	}
	
	private void moveShot(Figure figure) {
		float x = figure.getPosition().getX();
		float y = figure.getPosition().getY();
		figure.setPosition(x + figure.getDirection().getX() * figure.getVelocity(), y + figure.getDirection().getY() * figure.getVelocity());
	}

	// moves the chosen unit
	private void moveUnit(Unit unit) {
		float x = unit.getPosition().getX();
		float y = unit.getPosition().getY();
		unit.setPosition(x + unit.getDirection().getX() * unit.getVelocity(), y + unit.getDirection().getY() * unit.getVelocity());
	}

	/**
	 *  sets the game view
	 * @param gameView
	 */
	public void setView (GameView gameView) {
		this.gameView = gameView;
		setUserRequestListener();
	}
	
	/**
	 *  set the user request listener.
	 */
	public void setUserRequestListener() {
		gameView.setUserRequestListener(new UserRequestListener() {
			
			public void onNewModelCreationRequest (int xTouchPosition, int yTouchPosition) {
				proxy.performNewModelCreationRequest(xTouchPosition, yTouchPosition);
			}
			
			public void onGameExit() {
				proxy.performGameExit();
			}
		});
	}
	
	/**
	 * sets the GameModel
	 */
	public void setModels (GameModel models) {
		this.models = models;
	}

	@Override
	public void run() {
		points = (TextView)((Activity)this.context).findViewById(R.id.scorePoints);
        playerHitPoints = (TextView)((Activity)this.context).findViewById(R.id.scoreHitPointsPlayer);
        enemyHitPoints = (TextView)((Activity)this.context).findViewById(R.id.scoreHitPointsEnemy);
		gameLoop();
	}
	
	// reads the background map and builds the map matrix
	public void readMapFromFile(int backgroundToDraw) {
		InputStream is = context.getResources().openRawResource(backgroundToDraw);
		BufferedReader br =  new BufferedReader(new InputStreamReader(is), 1024*8);
	    try {
			buildMap(br);
		} catch (FileNotFoundException e) {
			System.out.println("failed to open file");
		} catch (IOException e) {
			System.out.println("failed to open file");
		}
	}
	
	// Builds a map from the given bufferedReader on the background matrix 
	private void buildMap(BufferedReader mapBuffer) throws IOException {
		String nextLine;
		int rows;
		int columns;
		//Gets the number of raws
		nextLine = mapBuffer.readLine();
		columns = Integer.parseInt(nextLine.toString());
		//Gets the number of columns
		nextLine = mapBuffer.readLine();
		rows = Integer.parseInt(nextLine);
		gameView.initBackground(columns, rows);
		//Goes through every raw and creates the background matrix out of the buffer
		for (int i = 0; i < rows; i++) {
			nextLine = mapBuffer.readLine();
			tileBuildRaw(nextLine.toString(), i);
		}
		Log.v("Msg:","finished to build map");
	}

	// builds a single raw in the map matrix
	private void tileBuildRaw(String line, int rowNumber) {
		for (int i = 0; i < line.length(); i++) {
			switch(line.charAt(i)){
				case 'g':
					gameView.setBackgroundTile(TileType.grass, i, rowNumber);
					break;
				case 'r':
					gameView.setBackgroundTile(TileType.road, i, rowNumber);
					break;
				case 's':
					gameView.setBackgroundTile(TileType.sea, i, rowNumber);
					break;
				case 'k':
					gameView.setBackgroundTile(TileType.rocks, i, rowNumber);
					break;
				case 'm':
					gameView.setBackgroundTile(TileType.mulch, i, rowNumber);
					break;
			}
		}
	}

	// sets the player points
	public void setPlayerPointsInt(int playerPointsInt) {
		this.playerPointsInt = playerPointsInt;
	}

	// gets the player points
	public int getPlayerPointsInt() {
		return playerPointsInt;
	}
	
	// sets the player hit points
	public void setPlayerHitPointsInt(int playerHitPointsInt) {
		this.playerHitPointsInt = playerHitPointsInt;
	}
	
	// gets the player hit points
	public int getPlayerHitPointsString() {
		return playerHitPointsInt;
	}

	// sets the enemy hit points
	public void setEnemyHitPointsInt(int enemyHitPointsInt) {
		this.enemyHitPointsInt = enemyHitPointsInt;
	}

	// gets the enemy hit points
	public int getEnemyHitPointsInt() {
		return enemyHitPointsInt;
	}

	// gets the massage to display
	public String getMessagToDisplay() {
		return messageToDisplay;
	}

	// sets the massage to display
	public void setMessagToDisplay(String messagToDisplay) {
		this.messageToDisplay = messagToDisplay;
	}
}
